分类
联系方式
  1. 新浪微博
  2. E-mail

QuteBrowser scrollcommands 滚动命令实现原理

介绍

QuteBrowser 表面上是一个 Vim 风格的浏览器,内部则是一套类编辑器(Vim、Emacs)的命令模式。各个快捷键背后都是命令(函数),在运行时由用户来触发。QuteBrowser 使用 Python 编写,使得我有机会一探究竟。本文介绍与滚动相关命令的实现。

对应的文件路径是:qutebrowser\components\scrollcommands.py

注册命令

只要给函数添加注解,就能向系统中注册任务:

@cmdutils.register()
@cmdutils.argument('tab', value=cmdutils.Value.cur_tab)
@cmdutils.argument('count', value=cmdutils.Value.count)
def scroll_px(tab: apitypes.Tab, dx: int, dy: int, count: int = 1) -> None:
    # ……

其中:

  • @cmdutils.register 负责注册命令
  • @cmdutils.argument 负责绑定相关参数

所有注册指令

cmdutils.register 关键词全局搜索,这一快速找出 QuteBrowser 中所有的命令。

按像素滚动

具体实现如下:

@cmdutils.register()
@cmdutils.argument('tab', value=cmdutils.Value.cur_tab)
@cmdutils.argument('count', value=cmdutils.Value.count)
def scroll_px(tab: apitypes.Tab, dx: int, dy: int, count: int = 1) -> None:
    """Scroll the current tab by 'count * dx/dy' pixels.

    Args:
        dx: How much to scroll in x-direction.
        dy: How much to scroll in y-direction.
        count: multiplier
    """
    dx *= count
    dy *= count
    cmdutils.check_overflow(dx, 'int')
    cmdutils.check_overflow(dy, 'int')
    tab.scroller.delta(dx, dy)

其中:

  • tab 参数的类型:
    • 对于交互命令是 cmdutils.Value.cur_tab
    • 对于函数参数是 apitypes.Tab
  • count 的类型 cmdutils.Value.count 看起来只是表示一个数字。

在代码实现中,经过计算和边界检查,最终调用 tab.scroller.delta(dx, dy) 完成具体 UI 操作。其中:

  • tab 类型为 apitypes.Tab,这就是 Tab 对应的 Widget。
  • tab.scroller 就是对应页面负责滚动的类。

滚动到锚点

再看一个滚动到锚点的命令:

@cmdutils.register()
@cmdutils.argument('tab', value=cmdutils.Value.cur_tab)
def scroll_to_anchor(tab: apitypes.Tab, name: str) -> None:
    """Scroll to the given anchor in the document.

    Args:
        name: The anchor to scroll to.
    """
    tab.scroller.before_jump_requested.emit()
    tab.scroller.to_anchor(name)

其中:

  • before_jump_requested 信号的作用,是告知 scroller 在跳转前记录当前位置,以便还能跳回来
  • to_anchor 则是实际进行锚点跳转

缺失的参数呢?

在两个函数中可以看到,都有部分参数没有给定:

  • scroll_to_anchor 的 name
  • scroll_px 的 dx 和 dy

这些参数都没有通过 @cmdutils.argument 注册,想必是不受用户控制的。

以 scroll_px 为例,把程序运行起来实验:

  • 在交互式命令中能够找到 scroll_px
  • 进行交互式调用,会提示我:the following arguments are required: dx, dy

从中可以看出,所有的注册过的命令都可以交互使用,但不是所有的命令在交互都支持交互使用